/*
REWRITTEN BY XINEF
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "ScriptedEscortAI.h"
#include "black_temple.h"

enum Says
{
	SAY_ILLIDAN_MINION					= 0,
	SAY_ILLIDAN_KILL					= 1,
	SAY_ILLIDAN_TAKEOFF					= 2,
	SAY_ILLIDAN_SUMMONFLAMES			= 3,
	SAY_ILLIDAN_EYE_BLAST				= 4,
	SAY_ILLIDAN_MORPH					= 5,
	SAY_ILLIDAN_ENRAGE					= 6,
	SAY_ILLIDAN_TAUNT					= 7,
	SAY_ILLIDAN_AKAMA1					= 8,
	SAY_ILLIDAN_AKAMA2					= 9,
	SAY_ILLIDAN_AKAMA3					= 10,
	SAY_ILLIDAN_MAIEV1					= 11,
	SAY_ILLIDAN_MAIEV2					= 12,
	SAY_ILLIDAN_MAIEV3					= 13,
	SAY_ILLIDAN_FRENZY					= 14,

	SAY_UDALO							= 0,
	SAY_OLUM							= 0,
	SAY_AKAMA_DOORS						= 0,
	SAY_AKAMA_FAIL						= 1,
	SAY_AKAMA_BEWARE					= 2,
	SAY_AKAMA_LEAVE						= 3,
	SAY_AKAMA_ILLIDAN1					= 4,
	SAY_AKAMA_ILLIDAN2					= 5,
	SAY_AKAMA_ILLIDAN3					= 6,

	SAY_MAIEV_SHADOWSONG_TAUNT			= 0,
	SAY_MAIEV_SHADOWSONG_ILLIDAN1		= 1,
	SAY_MAIEV_SHADOWSONG_ILLIDAN2		= 2,
	SAY_MAIEV_SHADOWSONG_ILLIDAN3		= 3,
	SAY_MAIEV_SHADOWSONG_ILLIDAN4		= 4,
	SAY_MAIEV_SHADOWSONG_ILLIDAN5		= 5,

	EMOTE_AZZINOTH_GAZE					= 0
};

enum Spells
{
	// Phase 1
	SPELL_DUAL_WIELD					= 42459,
	SPELL_BERSERK						= 45078,
	SPELL_FLAME_CRASH					= 40832,
	SPELL_DRAW_SOUL						= 40904,
	SPELL_DRAW_SOUL_HEAL				= 40903,
	SPELL_PARASITIC_SHADOWFIEND			= 41917,
	SPELL_PARASITIC_SHADOWFIEND_TRIGGER	= 41914,
	SPELL_SUMMON_PARASITIC_SHADOWFIENDS	= 41915,

	// Phase 2
	SPELL_THROW_GLAIVE					= 39635,
	SPELL_THROW_GLAIVE2					= 39849,
	SPELL_GLAIVE_RETURNS				= 39873,
	SPELL_SUMMON_GLAIVE					= 41466,
	SPELL_FIREBALL						= 40598,
	SPELL_DARK_BARRAGE					= 40585,
	SPELL_EYE_BLAST						= 39908,
	SPELL_UNCAGED_WRATH					= 39869,
	SPELL_FLAME_BLAST					= 40631,
	SPELL_CHARGE						= 42003,

	// Phase 3
	SPELL_AGONIZING_FLAMES				= 40932,
	SPELL_SUMMON_MAIEV					= 40403,
	SPELL_SHADOW_PRISON					= 40647,
	SPELL_TELEPORT_VISUAL_ONLY			= 41232,

	// Phase 4
	SPELL_DEMON_TRANSFORM_1				= 40511,
	SPELL_DEMON_TRANSFORM_2				= 40398,
	SPELL_DEMON_TRANSFORM_3				= 40510,
	SPELL_DEMON_FORM					= 40506,
	SPELL_SHADOW_BLAST					= 41078,
	SPELL_FLAME_BURST					= 41126,
	SPELL_FLAME_BURST_EFFECT			= 41131,
	SPELL_SUMMON_SHADOW_DEMON			= 41117,
	SPELL_CONSUME_SOUL					= 41080,
	SPELL_FIND_TARGET					= 41081,
	
	// Phase 5
	SPELL_FRENZY						= 40683,	
	SPELL_TELEPORT_MAIEV				= 41221,
	SPELL_SHADOW_STRIKE					= 40685,
	SPELL_THROW_DAGGER					= 41152,
	SPELL_DEATH							= 41220,

	// Cage
	SPELL_CAGED_DEBUFF					= 40695,
	SPELL_CAGED_SUMMON1					= 40696,
	SPELL_CAGED_SUMMON8					= 40703,
	SPELL_CAGE_TRAP						= 40760
};

enum Misc
{
	ACTION_FIGHT_MINIONS			= 1,
	ACTION_RETURN_BLADE				= 2,
	ACTION_ILLIDAN_CAGED			= 3,
	ACTION_ILLIDAN_DEAD				= 4,
	ACTION_MAIEV_SET_DIST30			= 5,
	ACTION_MAIEV_SET_DIST0			= 6,
	ACTION_MAIEV_OUTRO				= 7,

	MAX_EYE_BEAM_POS				= 4,

	POINT_ILLIDAN_HOVER				= 10,
	POINT_ILLIDAN_MIDDLE			= 11,

	NPC_ILLIDAN_DB_TARGET			= 23070,
	NPC_MAIEV_SHADOWSONG			= 23197,

    GO_CAGE_TRAP					= 185916
};

enum Events
{
	EVENT_SPELL_FLAME_CRASH				= 1,
	EVENT_SPELL_BERSERK					= 2,
	EVENT_SPELL_DRAW_SOUL				= 3,
	EVENT_SPELL_PARASITIC_SHADOWFIEND	= 4,
	EVENT_SPELL_AGONIZING_FLAMES		= 5,
	EVENT_SPELL_FRENZY					= 6,

	EVENT_SUMMON_MINIONS				= 20,
	EVENT_SUMMON_MINIONS2				= 21,

	EVENT_PHASE_2_START					= 40,
	EVENT_SPELL_FIREBALL				= 41,
	EVENT_SPELL_DARK_BARRAGE			= 42,
	EVENT_START_PHASE_2_END				= 43,
	EVENT_START_PHASE_2_WEAPON			= 44,
	EVENT_START_PHASE_3_LAND			= 45,
	EVENT_PHASE_2_SUMMON1				= 46,
	EVENT_PHASE_2_SUMMON2				= 47,
	EVENT_PHASE_2_EYE_BEAM				= 48,
	EVENT_PHASE_2_EYE_BEAM_START		= 49,
	EVENT_PHASE_2_CHANGE_POS			= 50,
	EVENT_PHASE_2_INTERRUPT				= 51,

	EVENT_PHASE_4_START					= 60,
	EVENT_PHASE_5_START					= 61,
	EVENT_PHASE_5_SCENE1				= 62,
	EVENT_PHASE_5_SCENE2				= 63,
	EVENT_PHASE_5_SCENE3				= 64,
	EVENT_PHASE_5_SCENE4				= 65,
	EVENT_PHASE_5_SCENE5				= 66,

	EVENT_REMOVE_DEMON_FORM				= 80,
	EVENT_SPELL_FLAME_BURST				= 81,
	EVENT_SPELL_SHADOW_BLAST			= 82,
	EVENT_SPELL_SHADOW_DEMONS			= 83,
	EVENT_MOVE_MAIEV					= 84,
	EVENT_FINISH_TRANSFORM				= 85, // Dummy, counter only

	EVENT_OUTRO_DEMON					= 90,
	EVENT_OUTRO_1						= 91,
	EVENT_OUTRO_2						= 92,
	EVENT_OUTRO_3						= 93,

	EVENT_KILL_TALK						= 100,
	EVENT_SAY_TAUNT						= 101,

	GROUP_PHASE_2_ABILITY				= 1
};

const Position eyeBeamPos[MAX_EYE_BEAM_POS*2] = 
{
	{639.97f, 301.63f, 354.0f, 0.0f},
	{658.83f, 265.10f, 354.0f, 0.0f},
	{656.86f, 344.07f, 354.0f, 0.0f},
	{640.70f, 310.47f, 354.0f, 0.0f},

	{706.22f, 273.26f, 354.0f, 0.0f},
	{717.55f, 328.33f, 354.0f, 0.0f},
	{718.06f, 286.08f, 354.0f, 0.0f},
	{705.92f, 337.14f, 354.0f, 0.0f}
};

const Position airHoverPos[MAX_EYE_BEAM_POS] = 
{
	{658.83f, 265.10f, 356.0f, 0.0f},
	{706.22f, 273.26f, 356.0f, 0.0f},
	{705.92f, 337.14f, 356.0f, 0.0f},
	{656.86f, 344.07f, 356.0f, 0.0f}
};

class boss_illidan_stormrage : public CreatureScript
{
	public:
		boss_illidan_stormrage() : CreatureScript("boss_illidan_stormrage") { }

		struct boss_illidan_stormrageAI : public BossAI
		{
			boss_illidan_stormrageAI(Creature* creature) : BossAI(creature, DATA_ILLIDAN_STORMRAGE)
			{
			}

			EventMap events2;
			uint8 beamPosId;

			void Reset()
			{
				BossAI::Reset();
				events2.Reset();
				me->SetDisableGravity(false);
				me->CastSpell(me, SPELL_DUAL_WIELD, true);
				me->LoadEquipment(0, true);
				me->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_IMMUNE_TO_NPC);
				beamPosId = urand(0, 3);
			}

			void EnterEvadeMode()
			{
				BossAI::EnterEvadeMode();

				if (Creature* akama = ObjectAccessor::GetCreature(*me, instance->GetData64(NPC_AKAMA)))
					akama->AI()->EnterEvadeMode();
			}

			bool CanAIAttack(const Unit* target) const
			{
				return target->GetEntry() != NPC_AKAMA && target->GetEntry() != NPC_MAIEV_SHADOWSONG;
			}

			void DoAction(int32 param)
			{
				if (param == ACTION_ILLIDAN_CAGED)
				{
					me->RemoveAurasDueToSpell(SPELL_FRENZY);
					events.Reset();
					events.ScheduleEvent(EVENT_PHASE_4_START, 16000);
				}
			}

			void JustSummoned(Creature* summon)
			{
				summons.Summon(summon);
				if (summon->GetEntry() == NPC_ILLIDAN_DB_TARGET)
					me->CastSpell(summon, SPELL_EYE_BLAST, false);
				else if (summon->GetEntry() == NPC_MAIEV_SHADOWSONG)
				{
					me->SetTarget(summon->GetGUID());
					me->SetFacingToObject(summon);
					summon->SetFacingToObject(me);
					summon->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
					summon->SetReactState(REACT_PASSIVE);
					summon->CastSpell(summon, SPELL_TELEPORT_VISUAL_ONLY, true);
				}
			}

			void SummonedCreatureDies(Creature* summon, Unit*)
			{
				summons.Despawn(summon);
			}

			void SummonedCreatureDespawn(Creature* summon)
			{
				summons.Despawn(summon);
			}

			void MovementInform(uint32 type, uint32 id)
			{
				if (type != POINT_MOTION_TYPE)
					return;

				if (id == POINT_ILLIDAN_HOVER)
				{
					if (events2.GetNextEventTime(EVENT_START_PHASE_2_END) == 0)
					{
						events.ScheduleEvent(EVENT_PHASE_2_SUMMON2, 2000);
						events.ScheduleEvent(EVENT_PHASE_2_CHANGE_POS, 6000);
						events.ScheduleEvent(EVENT_PHASE_2_EYE_BEAM, 15000, GROUP_PHASE_2_ABILITY);
						events2.ScheduleEvent(EVENT_START_PHASE_2_END, 10000);
					}
					me->SetFacingTo(me->GetAngle(676.02f, 305.45f));
				}
				else if (id == POINT_ILLIDAN_MIDDLE)
				{
                    EntryCheckPredicate pred(NPC_BLADE_OF_AZZINOTH);
					summons.DoAction(ACTION_RETURN_BLADE, pred);
					events.ScheduleEvent(EVENT_START_PHASE_2_WEAPON, 1500);
					events.ScheduleEvent(EVENT_START_PHASE_3_LAND, 4000);
				}
			}

			void ScheduleNormalEvents(uint8 phase)
			{
				events.ScheduleEvent(EVENT_SPELL_FLAME_CRASH, 15000);
				events.ScheduleEvent(EVENT_SPELL_DRAW_SOUL, 30000);
				events.ScheduleEvent(EVENT_SPELL_PARASITIC_SHADOWFIEND, 20000);
				events.ScheduleEvent(EVENT_SAY_TAUNT, 40000);
				if (phase >= 3)
				{
					events.ScheduleEvent(EVENT_PHASE_4_START, 60000);
					events.ScheduleEvent(EVENT_SPELL_AGONIZING_FLAMES, 10000);
				}
				if (phase >= 5)
					events.ScheduleEvent(EVENT_SPELL_FRENZY, 40000);
			}

			void EnterCombat(Unit* who)
			{
				summons.DespawnAll();
				BossAI::EnterCombat(who);
				ScheduleNormalEvents(1);
				events.ScheduleEvent(EVENT_SPELL_BERSERK, 25*MINUTE*IN_MILLISECONDS);
				events.ScheduleEvent(EVENT_SUMMON_MINIONS, 1000);
				events.ScheduleEvent(EVENT_PHASE_2_START, 1000);
			}

			void AttackStart(Unit* victim)
			{
				if (victim && me->Attack(victim, true))
					me->GetMotionMaster()->MoveChase(victim, events.GetNextEventTime(EVENT_REMOVE_DEMON_FORM) != 0 ? 35.0f : 0.0f);
			}

			void MoveInLineOfSight(Unit*) { }

			void JustDied(Unit* killer)
			{
				me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_NON_ATTACKABLE);
				summons.DespawnEntry(NPC_PARASITIC_SHADOWFIEND);
				instance->SetBossState(DATA_ILLIDAN_STORMRAGE, DONE);
				instance->SaveToDB();
			}

			void KilledUnit(Unit* victim)
			{
				if (events.GetNextEventTime(EVENT_KILL_TALK) == 0)
				{
					Talk(SAY_ILLIDAN_KILL);
					events.ScheduleEvent(EVENT_KILL_TALK, 6000);
				}
			}

			void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask)
			{
				if (damage >= me->GetHealth())
				{
					damage = 0;

					// xinef: do not allow to start outro when transforming
					if (events.GetNextEventTime(EVENT_FINISH_TRANSFORM))
						return;

					if (!me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
					{
						if (Creature* maiev = summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG))
						{
							maiev->StopMovingOnCurrentPos();
							maiev->GetMotionMaster()->Clear();
							maiev->SetReactState(REACT_PASSIVE);
							maiev->SetFullHealth();
							maiev->RemoveAllAuras();
							maiev->CombatStop();
						}

						if (events.GetNextEventTime(EVENT_REMOVE_DEMON_FORM) != 0)
						{
							me->CastSpell(me, SPELL_DEMON_TRANSFORM_1, true);
							events2.ScheduleEvent(EVENT_OUTRO_DEMON, 12000);
						}
						else
						{
							me->CastSpell(me, SPELL_TELEPORT_MAIEV, true);
							me->CastSpell(me, SPELL_DEATH, true);
							events2.ScheduleEvent(EVENT_OUTRO_1, 1000);
						}

						if (Creature* maiev = summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG))
						{
							maiev->SetTarget(me->GetGUID());
							maiev->SetFacingToObject(me);
						}

						events.Reset();
						me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
					}
				}
			}

			void UpdateAI(uint32 diff)
			{
				events2.Update(diff);
				switch (events2.ExecuteEvent())
				{
					case EVENT_SUMMON_MINIONS2:
						if (Creature* akama = ObjectAccessor::GetCreature(*me, instance->GetData64(NPC_AKAMA)))
							akama->AI()->DoAction(ACTION_FIGHT_MINIONS);
						break;
					case EVENT_PHASE_2_EYE_BEAM_START:
						Talk(SAY_ILLIDAN_EYE_BLAST);
						if (Creature* trigger = summons.GetCreatureWithEntry(NPC_ILLIDAN_DB_TARGET))
							trigger->GetMotionMaster()->MovePoint(0, eyeBeamPos[beamPosId+MAX_EYE_BEAM_POS], false, true);
						break;
					case EVENT_PHASE_2_INTERRUPT:
						me->InterruptNonMeleeSpells(false);
						break;
					case EVENT_START_PHASE_2_END:
						summons.RemoveNotExisting();
						if (!summons.HasEntry(NPC_FLAME_OF_AZZINOTH))
						{
							events.Reset();
							events2.Reset();
							me->InterruptNonMeleeSpells(false);
							me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_MIDDLE, 676.02f, 305.45f, 353.6f);
							break;
						}
						events2.ScheduleEvent(EVENT_START_PHASE_2_END, 1000);
						break;
					case EVENT_OUTRO_DEMON:
						me->CastSpell(me, SPELL_TELEPORT_MAIEV, true);
						me->CastSpell(me, SPELL_DEATH, true);
						events2.ScheduleEvent(EVENT_OUTRO_1, 1000);
						break;
					case EVENT_OUTRO_1:
						if (Creature* maiev = summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG))
						{
							maiev->SetTarget(me->GetGUID());
							maiev->SetFacingToObject(me);
							maiev->AI()->DoAction(ACTION_MAIEV_OUTRO);
							maiev->AI()->Talk(SAY_MAIEV_SHADOWSONG_ILLIDAN3);
						}
						
						if (Creature* akama = ObjectAccessor::GetCreature(*me, instance->GetData64(NPC_AKAMA)))
						{
							akama->AI()->DoAction(ACTION_ILLIDAN_DEAD);
							akama->SetTarget(me->GetGUID());
							akama->GetMotionMaster()->MovePoint(0, 695.63f, 306.63f, 354.26f);
						}
						events2.ScheduleEvent(EVENT_OUTRO_2, 6000);
						break;
					case EVENT_OUTRO_2:
						Talk(SAY_ILLIDAN_MAIEV3);
						events2.ScheduleEvent(EVENT_OUTRO_3, 17000);
						break;
					case EVENT_OUTRO_3:
						Unit::Kill(NULL, me);
						break;
				}

				
                EnterEvadeIfOutOfCombatArea();

				if (!UpdateVictim())
					return;

				events.Update(diff);
				if (me->HasUnitState(UNIT_STATE_CASTING))
					return;

				switch (uint32 eventId = events.ExecuteEvent())
				{
					case EVENT_SUMMON_MINIONS:
						if (me->HealthBelowPct(90))
						{
							Talk(SAY_ILLIDAN_MINION);
							events2.ScheduleEvent(EVENT_SUMMON_MINIONS2, 10000);
							break;
						}
						events.ScheduleEvent(EVENT_SUMMON_MINIONS, 1000);
						break;
					// ///////////////////////////
					// PHASE 1, 3, 5
					// ///////////////////////////
					case EVENT_SPELL_BERSERK:
						Talk(SAY_ILLIDAN_ENRAGE);
						me->CastSpell(me, SPELL_BERSERK, true);
						break;
					case EVENT_SPELL_FLAME_CRASH:
						me->CastSpell(me->GetVictim(), SPELL_FLAME_CRASH, false);
						events.ScheduleEvent(EVENT_SPELL_FLAME_CRASH, 25000);
						break;
					case EVENT_SPELL_DRAW_SOUL:
						me->CastSpell(me->GetVictim(), SPELL_DRAW_SOUL, false);
						events.ScheduleEvent(EVENT_SPELL_DRAW_SOUL, 40000);
						break;
					case EVENT_SPELL_PARASITIC_SHADOWFIEND:
						if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true))
							me->CastSpell(target, SPELL_PARASITIC_SHADOWFIEND, false);
						events.ScheduleEvent(EVENT_SPELL_PARASITIC_SHADOWFIEND, 30000);
						break;
					case EVENT_SAY_TAUNT:
						Talk(SAY_ILLIDAN_TAUNT);
						events.ScheduleEvent(EVENT_SAY_TAUNT, urand(30000, 60000));
						break;
					case EVENT_SPELL_FRENZY:
						Talk(SAY_ILLIDAN_FRENZY);
						me->CastSpell(me, SPELL_FRENZY, false);
						break;
					case EVENT_SPELL_AGONIZING_FLAMES:
						if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true))
							me->CastSpell(target, SPELL_AGONIZING_FLAMES, false);
						break;
					case EVENT_PHASE_5_START:
						if (me->HealthBelowPct(30))
						{
							me->CastSpell(me, SPELL_SHADOW_PRISON, true);
							me->SendMeleeAttackStop(me->GetVictim());
							me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_NON_ATTACKABLE);
							Talk(SAY_ILLIDAN_MAIEV1);
							events.Reset();
							events.ScheduleEvent(EVENT_PHASE_5_SCENE1, 9000);
							events.ScheduleEvent(EVENT_PHASE_5_SCENE2, 18000);
							events.ScheduleEvent(EVENT_PHASE_5_SCENE3, 24000);
							events.ScheduleEvent(EVENT_PHASE_5_SCENE4, 27000);
							events.ScheduleEvent(EVENT_PHASE_5_SCENE5, 30000);
							break;
						}
						events.ScheduleEvent(EVENT_PHASE_5_START, 1000);
						break;
					case EVENT_PHASE_5_SCENE1:
						me->CastSpell(me, SPELL_SUMMON_MAIEV, true);
						break;
					case EVENT_PHASE_5_SCENE2:
						Talk(SAY_ILLIDAN_MAIEV2);
						break;
					case EVENT_PHASE_5_SCENE3:
						if (Creature* maiev = summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG))
							maiev->AI()->Talk(SAY_MAIEV_SHADOWSONG_ILLIDAN2);
						break;
					case EVENT_PHASE_5_SCENE4:
						if (Creature* maiev = summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG))
							maiev->HandleEmoteCommand(EMOTE_ONESHOT_ROAR);
						break;
					case EVENT_PHASE_5_SCENE5:
						me->SetTarget(me->GetVictim()->GetGUID());
						me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_NON_ATTACKABLE);
						if (Creature* maiev = summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG))
						{
							maiev->SetReactState(REACT_AGGRESSIVE);
							maiev->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
							maiev->AI()->AttackStart(me);
						}
						ScheduleNormalEvents(5);
						break;
					// ///////////////////////////
					// PHASE 2
					// ///////////////////////////
					case EVENT_PHASE_2_START:
						if (me->HealthBelowPct(65))
						{
							if (events2.GetNextEventTime(EVENT_SUMMON_MINIONS2) != 0)
								events2.RescheduleEvent(EVENT_SUMMON_MINIONS2, 0);

							Talk(SAY_ILLIDAN_TAKEOFF);
							me->SendMeleeAttackStop(me->GetVictim());
							me->SetTarget(0);
							me->GetMotionMaster()->Clear();
							me->StopMovingOnCurrentPos();
							me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
							me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
							me->SetDisableGravity(true);

							events.Reset();
							events.ScheduleEvent(EVENT_PHASE_2_SUMMON1, 6000);
							break;
						}
						events.ScheduleEvent(EVENT_PHASE_2_START, 1000);
						break;
					case EVENT_PHASE_2_SUMMON1:
						me->LoadEquipment(1, true);
						me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_HOVER, 727.875f, 305.365f, 356.0f, false, true);
						break;
					case EVENT_PHASE_2_SUMMON2:
						Talk(SAY_ILLIDAN_SUMMONFLAMES);
						me->LoadEquipment(0, true);
						me->CastSpell(me, SPELL_THROW_GLAIVE, false);
						me->CastSpell(me, SPELL_THROW_GLAIVE2, false);
						break;
					case EVENT_PHASE_2_CHANGE_POS:
						beamPosId = (++beamPosId)%MAX_EYE_BEAM_POS;
						events.ScheduleEvent(EVENT_SPELL_FIREBALL, 8000, GROUP_PHASE_2_ABILITY);
						events.ScheduleEvent(EVENT_SPELL_DARK_BARRAGE, 18000, GROUP_PHASE_2_ABILITY);
						events.ScheduleEvent(EVENT_PHASE_2_EYE_BEAM, urand(25000, 50000), GROUP_PHASE_2_ABILITY);
						me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_HOVER, airHoverPos[beamPosId], false, true);
						break;
					case EVENT_PHASE_2_EYE_BEAM:
						me->SummonCreature(NPC_ILLIDAN_DB_TARGET, eyeBeamPos[beamPosId], TEMPSUMMON_TIMED_DESPAWN, 15000);
						events.CancelEventGroup(GROUP_PHASE_2_ABILITY);
						events.ScheduleEvent(EVENT_PHASE_2_CHANGE_POS, 20000);

						events2.ScheduleEvent(EVENT_PHASE_2_EYE_BEAM_START, 2000);
						events2.ScheduleEvent(EVENT_PHASE_2_INTERRUPT, 20000);
						break;
					case EVENT_SPELL_FIREBALL:
						if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true))
							me->CastSpell(target, SPELL_FIREBALL, false);
						events.ScheduleEvent(EVENT_SPELL_FIREBALL, 2200, GROUP_PHASE_2_ABILITY);
						break;
					case EVENT_SPELL_DARK_BARRAGE:
						if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true))
							me->CastSpell(target, SPELL_DARK_BARRAGE, false);
						events.ScheduleEvent(EVENT_SPELL_DARK_BARRAGE, 30000, GROUP_PHASE_2_ABILITY);
						break;
					case EVENT_START_PHASE_2_WEAPON:
						me->LoadEquipment(1, true);
						me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
						me->SetDisableGravity(false);
						break;
					case EVENT_START_PHASE_3_LAND:
						me->getThreatManager().resetAllAggro();
						me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
						me->SetTarget(me->GetVictim()->GetGUID());
						AttackStart(me->GetVictim());
						me->GetMotionMaster()->MoveChase(me->GetVictim());
						ScheduleNormalEvents(3);
						events.ScheduleEvent(EVENT_PHASE_5_START, 1000);
						break;
					// ///////////////////////////
					// PHASE 4
					// ///////////////////////////
					case EVENT_PHASE_4_START:
						me->CastSpell(me, SPELL_DEMON_TRANSFORM_1, true);
						me->getThreatManager().resetAllAggro();
						me->GetMotionMaster()->MoveChase(me->GetVictim(), 35.0f);
						events.Reset();
						events.ScheduleEvent(EVENT_SPELL_SHADOW_BLAST, 11000);
						events.ScheduleEvent(EVENT_MOVE_MAIEV, 5000);
						events.ScheduleEvent(EVENT_FINISH_TRANSFORM, 10500);
						events.ScheduleEvent(EVENT_SPELL_FLAME_BURST, 21000);
						events.ScheduleEvent(EVENT_SPELL_SHADOW_DEMONS, 36000);
						events.ScheduleEvent(EVENT_REMOVE_DEMON_FORM, 60000);
						break;
					case EVENT_SPELL_SHADOW_BLAST:
						me->CastSpell(me->GetVictim(), SPELL_SHADOW_BLAST, false);
						events.ScheduleEvent(EVENT_SPELL_SHADOW_BLAST, 2200);
						break;
					case EVENT_SPELL_FLAME_BURST:
						me->CastSpell(me, SPELL_FLAME_BURST, false);
						events.ScheduleEvent(EVENT_SPELL_FLAME_BURST, 22000);
						break;
					case EVENT_SPELL_SHADOW_DEMONS:
						me->CastSpell(me, SPELL_SUMMON_SHADOW_DEMON, false);
						break;
					case EVENT_REMOVE_DEMON_FORM:
						me->CastSpell(me, SPELL_DEMON_TRANSFORM_1, true);
						me->getThreatManager().resetAllAggro();
						events.Reset();
						if (summons.HasEntry(NPC_MAIEV_SHADOWSONG))
						{
							ScheduleNormalEvents(5);
							events.DelayEvents(11000);
							events.ScheduleEvent(EVENT_MOVE_MAIEV, 10000);
							events.ScheduleEvent(EVENT_FINISH_TRANSFORM, 10500);
						}
						else
						{
							ScheduleNormalEvents(3);
							events.ScheduleEvent(EVENT_PHASE_5_START, 1000);
							events.DelayEvents(11000);
							events.ScheduleEvent(EVENT_FINISH_TRANSFORM, 10500);
						}
						break;
					case EVENT_MOVE_MAIEV:
						if (Creature* maiev = summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG))
						{
							if (events.GetNextEventTime(EVENT_REMOVE_DEMON_FORM) != 0)
							{
								maiev->AI()->DoAction(ACTION_MAIEV_SET_DIST30);
								if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, -25.0f, true))
									maiev->GetMotionMaster()->MoveCharge(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 7.0f, 0);
								else
									maiev->GetMotionMaster()->MoveCharge(678.04f, 378.34f, 353.0f, 7.0f, 0);
							}
							else
							{
								maiev->AI()->DoAction(ACTION_MAIEV_SET_DIST0);
								maiev->GetMotionMaster()->MoveChase(me, 0.0f);
							}
						}
						break;
					case EVENT_FINISH_TRANSFORM:
						me->GetMotionMaster()->MoveChase(me->GetVictim(), events.GetNextEventTime(EVENT_REMOVE_DEMON_FORM) != 0 ? 35.0f : 0.0f);
						break;
				}

				if (!me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
					DoMeleeAttackIfReady();
            }
					
			bool CheckEvadeIfOutOfCombatArea() const
			{
				return me->GetHomePosition().GetExactDist(me) > 90.0f || !SelectTargetFromPlayerList(80.0f);
			}
		};

		CreatureAI* GetAI(Creature* creature) const
		{
			return GetInstanceAI<boss_illidan_stormrageAI>(creature);
		}
};

enum Akama
{
	POINT_DOORS					= 20,
	POINT_ILLIDAN				= 32,
	POINT_FIGHT_MINIONS			= 40,

	SPELL_AKAMA_DOOR_OPEN		= 41268,
	SPELL_AKAMA_DOOR_FAIL		= 41271,
    SPELL_DEATHSWORN_DOOR_OPEN	= 41269,
    SPELL_HEALING_POTION		= 40535,
    SPELL_CHAIN_LIGHTNING		= 40536,

    NPC_SPIRIT_OF_OLUM			= 23411,
    NPC_SPIRIT_OF_UDALO			= 23410,
	NPC_ILLIDARI_ELITE			= 23226,

	EVENT_AKAMA_SCENE_1			= 1,
	EVENT_AKAMA_SCENE_2			= 2,
	EVENT_AKAMA_SCENE_3			= 3,
	EVENT_AKAMA_SCENE_4			= 4,
	EVENT_AKAMA_SCENE_5			= 5,
	EVENT_AKAMA_SCENE_6			= 6,
	EVENT_AKAMA_SCENE_7			= 7,
	EVENT_AKAMA_SCENE_8			= 8,
	EVENT_AKAMA_SCENE_9			= 9,
	EVENT_AKAMA_SCENE_10		= 10,
	EVENT_AKAMA_SCENE_11		= 11,
	EVENT_AKAMA_SCENE_20		= 20,
	EVENT_AKAMA_SCENE_21		= 21,
	EVENT_AKAMA_SCENE_22		= 22,
	EVENT_AKAMA_SCENE_23		= 23,
	EVENT_AKAMA_SCENE_24		= 24,
	EVENT_AKAMA_SCENE_25		= 25,
	EVENT_AKAMA_SCENE_26		= 26,
	EVENT_AKAMA_SCENE_27		= 27,
	EVENT_AKAMA_SCENE_28		= 28,
	EVENT_AKAMA_SCENE_29		= 29,

	EVENT_AKAMA_SUMMON_ILLIDARI	= 100,
	EVENT_AKAMA_SPELL_CHAIN		= 101,
	EVENT_AKAMA_HEALTH			= 102
};

class npc_akama_illidan : public CreatureScript
{
	public:
		npc_akama_illidan() : CreatureScript("npc_akama_illidan") { }

		struct npc_akama_illidanAI : public npc_escortAI
		{
			npc_akama_illidanAI(Creature* creature) : npc_escortAI(creature), summons(me)
			{
				instance = creature->GetInstanceScript();
				if (instance->GetBossState(DATA_AKAMA_FINISHED) == DONE)
				{
					me->GetMap()->LoadGrid(751.664f, 238.933f);
					me->SetHomePosition(751.664f, 238.933f, 353.106f, 2.18f);
					me->NearTeleportTo(751.664f, 238.933f, 353.106f, 2.18f);
				}
			}

			void DoAction(int32 param)
			{
				if (param == ACTION_FIGHT_MINIONS)
				{
					me->CombatStop(true);
					events.ScheduleEvent(EVENT_AKAMA_SUMMON_ILLIDARI, 8000);
					events.ScheduleEvent(EVENT_AKAMA_SPELL_CHAIN, 7000);
					events.ScheduleEvent(EVENT_AKAMA_HEALTH, 1000);
					me->GetMotionMaster()->MoveCharge(741.97f, 358.74f, 353.0f, 10.0f, POINT_FIGHT_MINIONS);
					Talk(SAY_AKAMA_LEAVE);
				}
				else if (param == ACTION_ILLIDAN_DEAD)
				{
					events.Reset();
					summons.DespawnAll();
					me->CombatStop(true);
				}
			}

			void Reset()
			{
				me->SetReactState(REACT_AGGRESSIVE);
				me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
				me->setActive(false);
				me->SetVisible(instance->GetBossState(DATA_ILLIDARI_COUNCIL) == DONE && instance->GetBossState(DATA_ILLIDAN_STORMRAGE) != DONE);
				events.Reset();
				summons.DespawnAll();
			}

			void sGossipSelect(Player* player, uint32 /*sender*/, uint32 action)
			{
				player->CLOSE_GOSSIP_MENU();
				me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
				me->setActive(true);

				if (instance->GetBossState(DATA_AKAMA_FINISHED) != DONE)
				{
					me->SetReactState(REACT_PASSIVE);
					Start(false, true);
					SetDespawnAtEnd(false);
				}
				else
				{
					me->GetMotionMaster()->MovePoint(POINT_ILLIDAN, 744.45f, 304.84f, 353.0f);
					events.Reset();
					events.ScheduleEvent(EVENT_AKAMA_SCENE_20, 5000);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_21, 8000);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_22, 10000);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_23, 23000);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_24, 34000);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_25, 41000);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_26, 46000);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_27, 49000);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_28, 49200);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_29, 52000);
				}
			}

			void JustSummoned(Creature* summon)
			{
				summons.Summon(summon);
				summon->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
				if (summon->GetEntry() == NPC_ILLIDARI_ELITE)
				{
					me->AddThreat(summon, 1000000.0f);
					summon->AddThreat(me, 1000000.0f);
					summon->AI()->AttackStart(me);
					AttackStart(summon);
				}
			}

			void WaypointReached(uint32 pointId)
			{
				if (pointId == POINT_DOORS)
				{
					SetEscortPaused(true);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_1, 0);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_2, 4000);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_3, 7000);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_4, 17000);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_5, 23000);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_6, 25000);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_7, 31000);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_8, 40000);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_9, 54000);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_10, 57000);
					events.ScheduleEvent(EVENT_AKAMA_SCENE_11, 62000);
				}
				else if (pointId == POINT_ILLIDAN)
				{
					me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
					me->setActive(false);
					me->SetReactState(REACT_AGGRESSIVE);
				}
			}

			void MoveInLineOfSight(Unit* /*who*/) { }

			void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask)
			{
				if (damage >= me->GetHealth())
					damage = 0;
			}

			void UpdateEscortAI(uint32 diff)
			{
				events.Update(diff);
				switch (events.ExecuteEvent())
				{
					case EVENT_AKAMA_SCENE_1:
						me->SetFacingTo(0.0f);
						break;
					case EVENT_AKAMA_SCENE_2:
						Talk(SAY_AKAMA_DOORS);
						break;
					case EVENT_AKAMA_SCENE_3:
						me->CastSpell(me, SPELL_AKAMA_DOOR_FAIL, false);
						break;
					case EVENT_AKAMA_SCENE_4:
						Talk(SAY_AKAMA_FAIL);
						break;
					case EVENT_AKAMA_SCENE_5:
						me->SummonCreature(NPC_SPIRIT_OF_UDALO, me->GetPositionX()-5.0f, me->GetPositionY()+8.0f, me->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000);
						me->SummonCreature(NPC_SPIRIT_OF_OLUM, me->GetPositionX()-5.0f, me->GetPositionY()-8.0f, me->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000);
						break;
					case EVENT_AKAMA_SCENE_6:
						if (Creature* udalo = me->FindNearestCreature(NPC_SPIRIT_OF_UDALO, 15.0f))
							udalo->AI()->Talk(SAY_UDALO);
						break;
					case EVENT_AKAMA_SCENE_7:
						if (Creature* olum = me->FindNearestCreature(NPC_SPIRIT_OF_OLUM, 15.0f))
							olum->AI()->Talk(SAY_OLUM);
						break;
					case EVENT_AKAMA_SCENE_8:
						me->CastSpell(me, SPELL_AKAMA_DOOR_OPEN, false);
						if (Creature* olum = me->FindNearestCreature(NPC_SPIRIT_OF_OLUM, 15.0f))
							olum->CastSpell(olum, SPELL_AKAMA_DOOR_OPEN, false);
						if (Creature* udalo = me->FindNearestCreature(NPC_SPIRIT_OF_UDALO, 15.0f))
							udalo->CastSpell(udalo, SPELL_AKAMA_DOOR_OPEN, false);
						break;
					case EVENT_AKAMA_SCENE_9:
						instance->SetBossState(DATA_AKAMA_FINISHED, NOT_STARTED);
						instance->SetBossState(DATA_AKAMA_FINISHED, DONE);
						break;
					case EVENT_AKAMA_SCENE_10:
						Talk(SAY_AKAMA_BEWARE);
						break;
					case EVENT_AKAMA_SCENE_11:
						SetEscortPaused(false);
						break;
					case EVENT_AKAMA_SCENE_20:
						if (Creature* illidan = ObjectAccessor::GetCreature(*me, instance->GetData64(NPC_ILLIDAN_STORMRAGE)))
							illidan->SetStandState(UNIT_STAND_STATE_STAND);
						break;
					case EVENT_AKAMA_SCENE_21:
						me->SetFacingTo(M_PI);
						break;
					case EVENT_AKAMA_SCENE_22:
						if (Creature* illidan = ObjectAccessor::GetCreature(*me, instance->GetData64(NPC_ILLIDAN_STORMRAGE)))
							illidan->AI()->Talk(SAY_ILLIDAN_AKAMA1);
						break;
					case EVENT_AKAMA_SCENE_23:
						Talk(SAY_AKAMA_ILLIDAN1);
						break;
					case EVENT_AKAMA_SCENE_24:
						if (Creature* illidan = ObjectAccessor::GetCreature(*me, instance->GetData64(NPC_ILLIDAN_STORMRAGE)))
							illidan->AI()->Talk(SAY_ILLIDAN_AKAMA2);
						break;
					case EVENT_AKAMA_SCENE_25:
						Talk(SAY_AKAMA_ILLIDAN2);
						break;
					case EVENT_AKAMA_SCENE_26:
						if (Creature* illidan = ObjectAccessor::GetCreature(*me, instance->GetData64(NPC_ILLIDAN_STORMRAGE)))
							illidan->AI()->Talk(SAY_ILLIDAN_AKAMA3);
						break;
					case EVENT_AKAMA_SCENE_27:
						if (Creature* illidan = ObjectAccessor::GetCreature(*me, instance->GetData64(NPC_ILLIDAN_STORMRAGE)))
							illidan->LoadEquipment(1, true);
						break;
					case EVENT_AKAMA_SCENE_28:
						if (Creature* illidan = ObjectAccessor::GetCreature(*me, instance->GetData64(NPC_ILLIDAN_STORMRAGE)))
							illidan->HandleEmoteCommand(EMOTE_ONESHOT_TALK_NO_SHEATHE);
						break;
					case EVENT_AKAMA_SCENE_29:
						if (Creature* illidan = ObjectAccessor::GetCreature(*me, instance->GetData64(NPC_ILLIDAN_STORMRAGE)))
						{
							illidan->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_IMMUNE_TO_NPC);
							illidan->SetInCombatWithZone();
							AttackStart(illidan);
						}
						break;
					case EVENT_AKAMA_SUMMON_ILLIDARI:
						me->SummonCreature(NPC_ILLIDARI_ELITE, 742.55f, 359.12f, 353.0f, 4.35f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000);
						events.ScheduleEvent(EVENT_AKAMA_SUMMON_ILLIDARI, urand(2000, 6000));
						break;
					case EVENT_AKAMA_SPELL_CHAIN:
						if (me->GetVictim())
							me->CastSpell(me->GetVictim(), SPELL_CHAIN_LIGHTNING, false);
						events.ScheduleEvent(EVENT_AKAMA_SPELL_CHAIN, 20000);
						break;
					case EVENT_AKAMA_HEALTH:
						if (me->HealthBelowPct(20))
							me->CastSpell(me, SPELL_HEALING_POTION, false);
						events.ScheduleEvent(EVENT_AKAMA_HEALTH, 1000);
						break;
				}

				DoMeleeAttackIfReady();
			}

		private:
			EventMap events;
			SummonList summons;
			InstanceScript* instance;
		};

		CreatureAI* GetAI(Creature* creature) const
		{
			return GetInstanceAI<npc_akama_illidanAI>(creature);
		}
};

class spell_illidan_draw_soul : public SpellScriptLoader
{
	public:
		spell_illidan_draw_soul() : SpellScriptLoader("spell_illidan_draw_soul") { }

		class spell_illidan_draw_soul_SpellScript : public SpellScript
		{
			PrepareSpellScript(spell_illidan_draw_soul_SpellScript);

			void HandleScriptEffect(SpellEffIndex effIndex)
			{
				PreventHitDefaultEffect(effIndex);
				if (Unit* target = GetHitUnit())
					target->CastSpell(GetCaster(), SPELL_DRAW_SOUL_HEAL, true);
			}

			void Register()
			{
				OnEffectHitTarget += SpellEffectFn(spell_illidan_draw_soul_SpellScript::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
			}
		};

		SpellScript* GetSpellScript() const
		{
			return new spell_illidan_draw_soul_SpellScript();
		}
};

class spell_illidan_parasitic_shadowfiend : public SpellScriptLoader
{
	public:
		spell_illidan_parasitic_shadowfiend() : SpellScriptLoader("spell_illidan_parasitic_shadowfiend") { }

		class spell_illidan_parasitic_shadowfiend_AuraScript : public AuraScript
		{
			PrepareAuraScript(spell_illidan_parasitic_shadowfiend_AuraScript)

			void HandleEffectRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
			{
				if (!GetTarget()->HasAura(SPELL_SHADOW_PRISON) && GetTarget()->GetInstanceScript() && GetTarget()->GetInstanceScript()->IsEncounterInProgress())
					GetTarget()->CastSpell(GetTarget(), SPELL_SUMMON_PARASITIC_SHADOWFIENDS, true);
			}

			void Register()
			{
				AfterEffectRemove += AuraEffectRemoveFn(spell_illidan_parasitic_shadowfiend_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL);
			}
		};

		AuraScript* GetAuraScript() const
		{
			return new spell_illidan_parasitic_shadowfiend_AuraScript();
		}
};

class spell_illidan_parasitic_shadowfiend_trigger : public SpellScriptLoader
{
	public:
		spell_illidan_parasitic_shadowfiend_trigger() : SpellScriptLoader("spell_illidan_parasitic_shadowfiend_trigger") { }

		class spell_illidan_parasitic_shadowfiend_trigger_AuraScript : public AuraScript
		{
			PrepareAuraScript(spell_illidan_parasitic_shadowfiend_trigger_AuraScript)

			void HandleEffectRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
			{
				if (!GetTarget()->HasAura(SPELL_SHADOW_PRISON) && GetTarget()->GetInstanceScript() && GetTarget()->GetInstanceScript()->IsEncounterInProgress())
					GetTarget()->CastSpell(GetTarget(), SPELL_SUMMON_PARASITIC_SHADOWFIENDS, true);
			}

			void Register()
			{
				AfterEffectRemove += AuraEffectRemoveFn(spell_illidan_parasitic_shadowfiend_trigger_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL);
			}
		};

		AuraScript* GetAuraScript() const
		{
			return new spell_illidan_parasitic_shadowfiend_trigger_AuraScript();
		}

		class spell_illidan_parasitic_shadowfiend_trigger_SpellScript : public SpellScript
		{
			PrepareSpellScript(spell_illidan_parasitic_shadowfiend_trigger_SpellScript);

			void HandleScriptEffect(SpellEffIndex effIndex)
			{
				PreventHitDefaultEffect(effIndex);
				if (Creature* target = GetHitCreature())
					target->DespawnOrUnsummon(1);
			}

			void Register()
			{
				OnEffectHitTarget += SpellEffectFn(spell_illidan_parasitic_shadowfiend_trigger_SpellScript::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
			}
		};

		SpellScript* GetSpellScript() const
		{
			return new spell_illidan_parasitic_shadowfiend_trigger_SpellScript();
		}
};

class spell_illidan_glaive_throw : public SpellScriptLoader
{
    public:
        spell_illidan_glaive_throw() : SpellScriptLoader("spell_illidan_glaive_throw") { }

        class spell_illidan_glaive_throw_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_illidan_glaive_throw_SpellScript);

            void HandleDummy(SpellEffIndex effIndex)
            {
                PreventHitEffect(effIndex);
				if (Unit* target = GetHitUnit())
					target->CastSpell(target, SPELL_SUMMON_GLAIVE, true);
            }

            void Register()
            {
				OnEffectHitTarget += SpellEffectFn(spell_illidan_glaive_throw_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_illidan_glaive_throw_SpellScript();
        }
};

class spell_illidan_tear_of_azzinoth_summon_channel : public SpellScriptLoader
{
    public:
        spell_illidan_tear_of_azzinoth_summon_channel() : SpellScriptLoader("spell_illidan_tear_of_azzinoth_summon_channel") { }

        class spell_illidan_tear_of_azzinoth_summon_channel_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_illidan_tear_of_azzinoth_summon_channel_AuraScript);

            void OnPeriodic(AuraEffect const* aurEff)
            {
				PreventDefaultAction();
				if (Unit* caster = GetCaster())
				{
					if (GetTarget()->GetDistance2d(caster) > 25.0f)
					{
						SetDuration(0);
						GetTarget()->CastSpell(GetTarget(), SPELL_UNCAGED_WRATH, true);
					}
				}

				// xinef: ugly hax, dunno how it really works on blizz
				Map::PlayerList const& pl = GetTarget()->GetMap()->GetPlayers();
				for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
					if (Player* player = itr->GetSource())
						if (player->GetPositionX() > 693.4f || player->GetPositionY() < 271.8f || player->GetPositionX() < 658.43f || player->GetPositionY() > 338.68f)
						{
							GetTarget()->CastSpell(player, SPELL_CHARGE, true);
							break;
						}
            }

            void Register()
            {
                 OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_tear_of_azzinoth_summon_channel_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
            }
        };

        AuraScript* GetAuraScript() const
        {
            return new spell_illidan_tear_of_azzinoth_summon_channel_AuraScript();
		}
};

class spell_illidan_shadow_prison : public SpellScriptLoader
{
    public:
        spell_illidan_shadow_prison() : SpellScriptLoader("spell_illidan_shadow_prison") { }

        class spell_illidan_shadow_prison_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_illidan_shadow_prison_SpellScript);

            void FilterTargets(std::list<WorldObject*>& targets)
            {
				targets.remove_if(PlayerOrPetCheck());
            }

            void Register()
            {
                OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_illidan_shadow_prison_SpellScript::FilterTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ENEMY);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_illidan_shadow_prison_SpellScript();
        }
};

class spell_illidan_demon_transform1 : public SpellScriptLoader
{
    public:
        spell_illidan_demon_transform1() : SpellScriptLoader("spell_illidan_demon_transform1") { }

        class spell_illidan_demon_transform1_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_illidan_demon_transform1_AuraScript);

			bool Load()
			{
				return GetUnitOwner()->GetTypeId() == TYPEID_UNIT;
			}

            void OnPeriodic(AuraEffect const* aurEff)
            {
				PreventDefaultAction();
				SetDuration(0);
				GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_DEMON_TRANSFORM_2, true);
				GetUnitOwner()->ToCreature()->LoadEquipment(0, true);
            }

            void Register()
            {
                 OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_demon_transform1_AuraScript::OnPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
            }
        };

        AuraScript* GetAuraScript() const
        {
            return new spell_illidan_demon_transform1_AuraScript();
        }
};

class spell_illidan_demon_transform2 : public SpellScriptLoader
{
    public:
        spell_illidan_demon_transform2() : SpellScriptLoader("spell_illidan_demon_transform2") { }

        class spell_illidan_demon_transform2_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_illidan_demon_transform2_AuraScript);

			bool Load()
			{
				return GetUnitOwner()->GetTypeId() == TYPEID_UNIT;
			}

            void OnPeriodic(AuraEffect const* aurEff)
            {
				PreventDefaultAction();

				if (aurEff->GetTickNumber() == 1)
				{
					if (GetUnitOwner()->GetDisplayId() == GetUnitOwner()->GetNativeDisplayId())
						GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_DEMON_FORM, true);
					else
						GetUnitOwner()->RemoveAurasDueToSpell(SPELL_DEMON_FORM);
				}
				else if (aurEff->GetTickNumber() == 2)
				{
					SetDuration(0);
					GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_DEMON_TRANSFORM_3, true);
					if (Aura* aura = GetUnitOwner()->GetAura(SPELL_DEMON_TRANSFORM_3))
						aura->SetDuration(4500);

					if (!GetUnitOwner()->HasAura(SPELL_DEMON_FORM))
						GetUnitOwner()->ToCreature()->LoadEquipment(1, true);
				}
            }

            void Register()
            {
                 OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_demon_transform2_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
            }
        };

        AuraScript* GetAuraScript() const
        {
            return new spell_illidan_demon_transform2_AuraScript();
        }
};

class spell_illidan_flame_burst : public SpellScriptLoader
{
    public:
        spell_illidan_flame_burst() : SpellScriptLoader("spell_illidan_flame_burst") { }

        class spell_illidan_flame_burst_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_illidan_flame_burst_SpellScript);

            void HandleScriptEffect(SpellEffIndex effIndex)
            {
                PreventHitEffect(effIndex);
				if (Unit* target = GetHitUnit())
					target->CastSpell(target, SPELL_FLAME_BURST_EFFECT, true);
            }

            void Register()
            {
				OnEffectHitTarget += SpellEffectFn(spell_illidan_flame_burst_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_illidan_flame_burst_SpellScript();
        }
};

class spell_illidan_found_target : public SpellScriptLoader
{
    public:
        spell_illidan_found_target() : SpellScriptLoader("spell_illidan_found_target") { }

        class spell_illidan_found_target_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_illidan_found_target_SpellScript);

            void HandleDummy(SpellEffIndex effIndex)
            {
                PreventHitEffect(effIndex);
				if (Unit* target = GetHitUnit())
					if (GetCaster()->GetDistance(target) < 2.0f)
					{
						GetCaster()->CastSpell(target, SPELL_CONSUME_SOUL, true);
						GetCaster()->CastSpell(GetCaster(), SPELL_FIND_TARGET, true);
					}
            }

            void Register()
            {
				OnEffectHitTarget += SpellEffectFn(spell_illidan_found_target_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_illidan_found_target_SpellScript();
        }
};

class spell_illidan_cage_trap : public SpellScriptLoader
{
    public:
        spell_illidan_cage_trap() : SpellScriptLoader("spell_illidan_cage_trap") { }

        class spell_illidan_cage_trap_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_illidan_cage_trap_SpellScript);

			bool Load()
			{
				return GetCaster()->GetTypeId() == TYPEID_UNIT;
			}

            void HandleScriptEffect(SpellEffIndex effIndex)
            {
                PreventHitEffect(effIndex);
				if (Creature* target = GetHitCreature())
					if (GetCaster()->GetExactDist2d(target) < 4.0f)
					{
						target->AI()->DoAction(ACTION_ILLIDAN_CAGED);
						target->CastSpell(target, SPELL_CAGE_TRAP, true);
						GetCaster()->ToCreature()->DespawnOrUnsummon(1);
						if (GameObject* gobject = GetCaster()->FindNearestGameObject(GO_CAGE_TRAP, 10.0f))
							gobject->SetLootState(GO_JUST_DEACTIVATED);
					}
            }

            void Register()
            {
				OnEffectHitTarget += SpellEffectFn(spell_illidan_cage_trap_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_illidan_cage_trap_SpellScript();
        }
};

class spell_illidan_cage_trap_stun : public SpellScriptLoader
{
    public:
        spell_illidan_cage_trap_stun() : SpellScriptLoader("spell_illidan_cage_trap_stun") { }

        class spell_illidan_cage_trap_stun_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_illidan_cage_trap_stun_AuraScript);

            void OnPeriodic(AuraEffect const* aurEff)
            {
				PreventDefaultAction();
				SetDuration(0);

				for (uint32 i = SPELL_CAGED_SUMMON1; i <= SPELL_CAGED_SUMMON8; ++i)
					GetTarget()->CastSpell(GetTarget(), i, true);
				GetTarget()->CastSpell(GetTarget(), SPELL_CAGED_DEBUFF, true);
            }

            void Register()
            {
                 OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_cage_trap_stun_AuraScript::OnPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
            }
        };

        AuraScript* GetAuraScript() const
        {
            return new spell_illidan_cage_trap_stun_AuraScript();
        }
};

void AddSC_boss_illidan()
{
    new boss_illidan_stormrage();
    new npc_akama_illidan();
	new spell_illidan_draw_soul();
	new spell_illidan_parasitic_shadowfiend();
	new spell_illidan_parasitic_shadowfiend_trigger();
	new spell_illidan_glaive_throw();
	new spell_illidan_tear_of_azzinoth_summon_channel();
	new spell_illidan_shadow_prison();
	new spell_illidan_demon_transform1();
	new spell_illidan_demon_transform2();
	new spell_illidan_flame_burst();
	new spell_illidan_found_target();
	new spell_illidan_cage_trap();
	new spell_illidan_cage_trap_stun();
}
